---
title: "快速开始"
---

# Modern.js

:::tip

* 示例 Demo [Modern.js SSR](https://github.com/module-federation/module-federation-examples/tree/master/modernjs-ssr)

:::


## 环境准备

### Node.js

在开始使用前，你需要安装 [Node.js](https://nodejs.org/)，并保证 Node.js 版本不低于 16.2.0，**我们推荐使用 Node.js 18 的 LTS 版本**。

你可以通过以下命令检查当前使用的 Node.js 版本：

```bash
node -v
```

如果你当前的环境中尚未安装 Node.js，或是安装的版本低于 16，可以通过 [nvm](https://github.com/nvm-sh/nvm) 或 [fnm](https://github.com/Schniz/fnm) 安装需要的版本。

下面是通过 nvm 安装 Node.js 18 LTS 版本的例子：

```bash
# 安装 Node.js 18 的长期支持版本
nvm install 18 --lts

# 将刚安装的 Node.js 18 设置为默认版本
nvm alias default 18

# 切换到刚安装的 Node.js 18
nvm use 18
```

:::tip nvm 和 fnm
nvm 和 fnm 都是 Node.js 版本管理工具。相对来说，nvm 较为成熟和稳定，而 fnm 是使用 Rust 实现的，比 nvm 提供了更好的性能。
:::

此外，在安装 nvm 或 fnm 后，然后只要仓库根目录下有内容为 `lts/hydrogen` 的 `.nvmrc` 文件，进入这个仓库时就会自动安装或切换到正确的 Node.js 版本。


### pnpm

推荐使用 [pnpm](https://pnpm.io/installation) 来管理依赖：

```bash
npm install -g pnpm@8
```

:::note
Modern.js 同样支持使用 `yarn`、`npm` 进行依赖管理。

:::

## 创建项目

Modern.js 提供了 `@modern-js/create` 工具来创建项目，不需要全局安装，直接使用 `npx` 按需运行即可。

### 创建消费者

```bash
npx @modern-js/create@latest modern-consumer
```

### 创建生产者

```bash
npx @modern-js/create@latest modern-provider
```

### 安装插件

Module Federation 为 Modern.js 提供了 配套的插件 `@module-federation/modern-js`。

```bash
pnpm add @module-federation/modern-js
```

## 设置模块联邦配置

### 生产者

#### 1. 创建配置文件

在项目根目录创建 `module-federation.config.ts` 文件，并写入下列内容：

```ts title='module-federation.config.ts'
import { createModuleFederationConfig } from '@module-federation/modern-js';

export default createModuleFederationConfig({
  name: 'provider',
  filename: 'remoteEntry.js',
  exposes: {
    './Image': './src/components/Image.tsx',
  },
  shared: {
    react: { singleton: true },
    'react-dom': { singleton: true },
  },
});
```

#### 2. 应用插件

在 `modern.config.ts` 应用 `@module-federation/modern-js`：

```ts title='modern.config.ts'
import { appTools, defineConfig } from '@modern-js/app-tools';
import { moduleFederationPlugin } from '@module-federation/modern-js';

// https://modernjs.dev/en/configure/app/usage
export default defineConfig({
  runtime: {
    router: true,
  },
  server: {
    ssr: {
      mode: 'stream',
    },
    port: 3006,
  },
  plugins: [appTools(), moduleFederationPlugin()],
});
```

#### 3. 创建导出组件

创建文件 `src/components/Image.tsx` ，内容如下：

```tsx title='Image.tsx'
import React from 'react';
import styles from './Image.module.css';

export default (): JSX.Element => (
  <div
    id="remote-components"
    style={{
      backgroundColor: '#1ee9c1',
      color: 'lightgrey',
      padding: '1rem',
    }}
  >
    <h2>
      <strong>remote</strong>&nbsp;image
    </h2>
    <button
      id="remote-components-button"
      style={{ marginBottom: '1rem' }}
      onClick={() => alert('[remote-components] Client side Javascript works!')}
    >
      Click me to test i'm interactive!
    </button>
    <img
      id="remote-components-image"
      src="https://module-federation.io/module-federation-logo.svg"
      style={{ width: '100px' }}
      alt="serge"
    />
    <button className={styles['button']}>Button from remote</button>
  </div>
);
```
并创建对应的样式文件，内容如下：
```css title='Image.module.css'
.button {
  background: red;
}
```

### 消费者

#### 1. 创建配置文件

在项目根目录创建 `module-federation.config.ts` 文件，并写入下列内容：

```ts title='module-federation.config.ts'
import { createModuleFederationConfig } from '@module-federation/modern-js';

export default createModuleFederationConfig({
  name: 'consumer',
  remotes: {
    remote: 'provider@http://localhost:3006/mf-manifest.json',
  },
  shared: {
    react: { singleton: true },
    'react-dom': { singleton: true },
  },
});
```

#### 2. 应用插件

在 `modern.config.ts` 应用 `@module-federation/modern-js`：

```ts title='modern.config.ts'
import { appTools, defineConfig } from '@modern-js/app-tools';
import { moduleFederationPlugin } from '@module-federation/modern-js';

// https://modernjs.dev/en/configure/app/usage
export default defineConfig({
  runtime: {
    router: true,
  },
  server: {
    ssr: {
      mode: 'stream',
    },
    port: 3007,
  },
  plugins: [appTools(), moduleFederationPlugin()],
});
```
#### 3. 引用类型

在 `tsconfig.json` 添加 `paths` 以获取生产者的类型：

```json
{
  "compilerOptions": {
    "paths": {
      "*": ["./@mf-types/*"]
    }
  }
}
```
#### 4. 消费生产者

修改入口页面（`src/routes/page.tsx`），引用生产者提供的组件，内容如下：

```tsx title='page.tsx'
import ProviderImage from 'remote/Image';
import './index.css';

const Index = () => (
  <div className="container-box">
    <ProviderImage />
  </div>
);

export default Index;
```

## CSS 闪烁问题

启动项目访问 `http://localhost:3007/`，发现SSR 正常工作，页面可以正常渲染，但是会有样式闪烁的问题。

这是因为生产者的样式文件无法注入到对应的 html 中。

此问题可以通过使用 `@module-federation/modern-js` 提供的 [createremotessrcomponent](../../../guide/framework/modernjs#createremotessrcomponent) 解决。

修改消费者引用生产者处的代码（`src/routes/page.tsx`）：

```tsx title='page.tsx'
import { getInstance } from '@module-federation/modern-js/runtime';
import { createLazyComponent } from '@module-federation/modern-js/react'
import './index.css';

const RemoteSSRComponent = createLazyComponent({
  instance: getInstance(),
  loader: () => import('remote/Image'),
  loading: 'loading...',
  export: 'default',
  fallback: ({ error }) => {
    if (error instanceof Error && error.message.includes('not exist')) {
      return <div>fallback - not existed id</div>;
    }
    return <div>fallback</div>;
  },
});

const Index = () => (
  <div className="container-box">
    <RemoteSSRComponent />
  </div>
);

export default Index;
```

修改后重新访问页面，可以观测返回的 html 中会自动注入生产者的样式文件，从而解决 CSS 闪烁问题。
